package clientSide;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import common.Message;
import common.MessageType;

public class Client implements Runnable {
	// /Message queue
	List<Message> messages;
	// /socket
	private Socket socket;
	// stream to read message from server
	private ObjectInputStream inputStream;
	// /stream to send message to server
	private ObjectOutputStream outputStream;
	// continue to read message or not
	private boolean working = true;
	// /message to save to queue
	private Message msg;
	// determine whether client is closed or not
	public boolean Closed = false;
	// determine whether client is logined or not
	public boolean logined = false;

	private UserInfo user;

	public Client(String host, int port) {
		try {
			this.socket = new Socket(host, port);
			outputStream = new ObjectOutputStream(socket.getOutputStream());
			outputStream.flush();
			inputStream = new ObjectInputStream(socket.getInputStream());
			messages = new ArrayList<>();
			user = new UserInfo("user", "user", " ", 0, "noavatar");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (working) {
			try {
				if (inputStream == null)
					inputStream = new ObjectInputStream(socket.getInputStream());
				synchronized (inputStream) {
					msg = (Message) inputStream.readObject();
					messages.add(msg);
					if(logined)
					{
						Message deliver = new Message(user.getUserName(), "SERVER", MessageType.MessageDelivery, "receive".getBytes());
						deliver.setheader(msg.getHeader());
						deliver.setTime(msg.getTime());
						SendMessage(deliver);
					}
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				try {
					CloseClient();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				e.printStackTrace();
			}

		}
	}

	public synchronized void SendMessage(Message message) throws Exception {
		if (outputStream == null)
			throw new Exception("out put stream");
		if (message.getType() == MessageType.Logout) {
			CloseClient();
			working = false;
			return;
		}
		outputStream.writeObject(message);
		outputStream.flush();
	}

	public synchronized boolean HasMessage() {
		return messages.size() > 0;
	}

	public synchronized Message getMessage(boolean remove) throws Exception {
		if (!HasMessage())
			throw new Exception("Nore more message");
		Message r = messages.get(0);
		if (remove)
			messages.remove(0);
		return r;
	}

	public synchronized void RemoveMessage() {
		if (HasMessage())
			messages.remove(0);
	}

	public synchronized int getClientId() {
		return socket.getLocalPort();
	}

	public synchronized String getUsername() {
		return user.getUserName();
	}

	public synchronized void CloseClient() throws IOException {
		Closed = true;
		working = false;
		if (socket != null && !socket.isClosed())
			socket.close();
		if (inputStream != null)
			inputStream.close();
		if (outputStream != null)
			outputStream.close();
	}

	public synchronized void setUserName(String name) {
		user.setUserName(name);
	}

}
